namespace SimpleWebServices.Data
{
    using System;
    using System.Globalization;
    using System.Xml;
    using SimpleWebServices;
    using SimpleWebServices.Configuration;
    using SimpleWebServices.Web;
    using Xunit;

    public sealed class FakeRepositoryFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<FakeRepository>()
                .DerivesFrom<object>()
                .IsConcreteClass()
                .IsSealed()
                .HasDefaultConstructor()
                .Implements<IRepository>()
                .Result);
        }

        [Fact]
        public void repository()
        {
            Assert.True(new RepositoryExpectations<FakeRepository>().Result);
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new FakeRepository());
        }

        [Fact]
        public void prop_Xml_get()
        {
            string expected = "<repository />";
            string actual = new FakeRepository().Xml.CreateNavigator().OuterXml;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Register()
        {
            FakeRepository.Clear();

            try
            {
                string expected = "<repository />";

                string actual = FakeRepository.Register().Xml.CreateNavigator().OuterXml;

                Assert.Equal<string>(expected, actual);
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_Register_string()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository repository = FakeRepository.Register("<repository><object><test /></object></repository>");

                Assert.True((bool)repository.Xml.CreateNavigator().Evaluate("1=count(/repository/object/test)"));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_Register_stringEmpty()
        {
            FakeRepository.Clear();

            try
            {
                string expected = "<repository />";

                string actual = FakeRepository.Register(string.Empty).Xml.CreateNavigator().OuterXml;

                Assert.Equal<string>(expected, actual);
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_Register_stringNull()
        {
            FakeRepository.Clear();

            try
            {
                string expected = "<repository />";

                string actual = FakeRepository.Register(null as string).Xml.CreateNavigator().OuterXml;

                Assert.Equal<string>(expected, actual);
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_Register_objects()
        {
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();

                FakeRepository repository = FakeRepository.Register(new
                {
                    token = token,
                    urn = "urn://foo/bar",
                    created = DateTime.UtcNow,
                    modified = DateTime.UtcNow,
                    etag = "xyz",
                    xml = "<test />"
                });

                string xpath = "1=count(/repository/object[@token='" + token + "'][@urn='urn://foo/bar'][@etag='xyz'][@created][@modified]/test)";

                Assert.True((bool)repository.Xml.CreateNavigator().Evaluate(xpath));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_Register_objectsInvalid()
        {
            FakeRepository.Clear();

            try
            {
                Assert.Throws<NotSupportedException>(() => FakeRepository.Register(new { invalid = "invalid" }));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RegisterOfT()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository records = FakeRepository.Register<TestRecord>();

                Assert.Equal<string>(typeof(TestRecord).Name, RepositoryConfiguration.Settings()[0].Key);
                Assert.True((bool)records.Xml.CreateNavigator().Evaluate("1=count(/repository)"));
                Assert.NotNull(RepositoryConfiguration.Resolve<TestRecord>());
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RegisterOfT_string()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository records = FakeRepository.Register<TestRecord>("<repository><object><test /></object></repository>");

                Assert.Equal<string>(typeof(TestRecord).Name, RepositoryConfiguration.Settings()[0].Key);
                Assert.True((bool)records.Xml.CreateNavigator().Evaluate("1=count(/repository/object/test)"));
                Assert.NotNull(RepositoryConfiguration.Resolve<TestRecord>());
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RegisterOfT_stringEmpty()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository records = FakeRepository.Register<TestRecord>(string.Empty);

                Assert.Equal<string>(typeof(TestRecord).Name, RepositoryConfiguration.Settings()[0].Key);
                Assert.True((bool)records.Xml.CreateNavigator().Evaluate("1=count(/repository)"));
                Assert.NotNull(RepositoryConfiguration.Resolve<TestRecord>());
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RegisterOfT_stringNull()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository records = FakeRepository.Register<TestRecord>(null as string);

                Assert.Equal<string>(typeof(TestRecord).Name, RepositoryConfiguration.Settings()[0].Key);
                Assert.True((bool)records.Xml.CreateNavigator().Evaluate("1=count(/repository)"));
                Assert.NotNull(RepositoryConfiguration.Resolve<TestRecord>());
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RegisterOfT_objects()
        {
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();

                FakeRepository records = FakeRepository.Register<TestRecord>(new
                {
                    token = token,
                    urn = "urn://foo/bar",
                    created = DateTime.UtcNow,
                    modified = DateTime.UtcNow,
                    etag = "xyz",
                    xml = "<test />"
                });

                string xpath = "1=count(/repository/object[@token='" + token + "'][@urn='urn://foo/bar'][@etag='xyz'][@created][@modified]/test)";

                Assert.True((bool)records.Xml.CreateNavigator().Evaluate(xpath));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RegisterOfT_objectsInvalid()
        {
            FakeRepository.Clear();

            try
            {
                Assert.Throws<NotSupportedException>(() => FakeRepository.Register<TestRecord>(new { invalid = "invalid" }));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RegisterById_string()
        {
            FakeRepository.Clear();

            try
            {
                string expected = "<repository />";

                string actual = FakeRepository.RegisterById("id").Xml.CreateNavigator().OuterXml;

                Assert.Equal<string>(expected, actual);
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RegisterById_string_string()
        {
            FakeRepository.Clear();

            try
            {
                FakeRepository repository = FakeRepository.RegisterById("id", "<repository><object><test /></object></repository>");

                Assert.True((bool)repository.Xml.CreateNavigator().Evaluate("1=count(/repository/object/test)"));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RegisterById_string_stringEmpty()
        {
            FakeRepository.Clear();

            try
            {
                string expected = "<repository />";

                string actual = FakeRepository.RegisterById("id", string.Empty).Xml.CreateNavigator().OuterXml;

                Assert.Equal<string>(expected, actual);
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RegisterById_stringNull()
        {
            FakeRepository.Clear();

            try
            {
                string expected = "<repository />";

                string actual = FakeRepository.RegisterById("id", null as string).Xml.CreateNavigator().OuterXml;

                Assert.Equal<string>(expected, actual);
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RegisterById_string_objects()
        {
            FakeRepository.Clear();

            try
            {
                Token token = Token.New();

                FakeRepository repository = FakeRepository.RegisterById(
                    "id",
                    new { token = token, urn = "urn://foo/bar", created = DateTime.UtcNow, modified = DateTime.UtcNow, etag = "xyz", xml = "<test />" });

                string xpath = "1=count(/repository/object[@token='" + token + "'][@urn='urn://foo/bar'][@etag='xyz'][@created][@modified]/test)";

                Assert.True((bool)repository.Xml.CreateNavigator().Evaluate(xpath));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_RegisterById_string_objectsInvalid()
        {
            FakeRepository.Clear();

            try
            {
                Assert.Throws<NotSupportedException>(() => FakeRepository.RegisterById("id", new { invalid = "invalid" }));
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void op_LoadXml_stringNull()
        {
            Assert.Throws<ArgumentNullException>(() => new FakeRepository().LoadXml(null));
        }

        [Fact]
        public void op_LoadXml_stringEmpty()
        {
            Assert.Throws<XmlException>(() => new FakeRepository().LoadXml(string.Empty));
        }

        [Fact]
        public void op_LoadXml_stringInvalid()
        {
            Assert.Throws<XmlException>(() => new FakeRepository().LoadXml("I am not XML."));
        }

        [Fact]
        public void op_LoadXml_string()
        {
            Token token = Token.New();
            AbsoluteUri urn = "urn://fake/" + token;
            string xml = string.Format(CultureInfo.InvariantCulture, "<repository><object token='{0}' urn='{1}'><resource /></object></repository>", token, urn);

            IRepository repository = new FakeRepository();
            (repository as FakeRepository).LoadXml(xml);

            Assert.NotNull(repository.Select<TestResource>(urn));
        }
    }
}